package ro.generic.util;


/**
 * MD5 implementation based on Fast-MD5 algorithm
 *
 * @author Pinca George (papagalu)
 * @version 1.0, 10.06.2004
 */
public final class MD5 {

	//--------------------------------------------------------//
	//                        CONSTANTS                       //
	//--------------------------------------------------------//

	private static final byte padding[] = {
		(byte)0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	}; // pading for final

	//--------------------------------------------------------//
	//                          FIELDS                        //
	//--------------------------------------------------------//

	private MD5State state; // current state
	private MD5State finals; // final state

	//--------------------------------------------------------//
	//                      CONSTRUCTORS                      //
	//--------------------------------------------------------//

	/**
	 * Default constructor
	 */
	public MD5 () {
		init();
	}

	/**
	 * Constructor used to update hash with <i>obj.toString()</i>
	 *
	 * @param	obj	obj.toString() is used to update hash after initialization
	 */
	public MD5 (Object obj) {
		this();
		update(obj.toString());
	}

	/**
	 * Constructor used to update hash with the specified sub-array
	 *
	 * @param buffer array of bytes to use for updating the hash
	 * @param offset offset in array
	 * @param length number of bytes to be updated
	 */
	public MD5 (byte[] buffer, int offset, int length) {
		this();
		update(buffer, offset, length);
	}

	//--------------------------------------------------------//
	//                         METHODS                        //
	//--------------------------------------------------------//

	/**
	 * Initialize MD5 internal state; to reuse MD5 abject just by calling <i>init()</i> after every <i>result()</i> call
	 */
	public synchronized void init () {
		state = new MD5State();
		finals = null;
	}

	/**
	 * Updates hash with given array of bytes
	 *
	 * @param buffer array of bytes to use for updating the hash
	 */
	public void update (byte buffer[]) {
		update(buffer, 0, buffer.length);
	}

	/**
	 * Updates hash with given array of bytes
	 *
	 * @param buffer array of bytes to use for updating the hash
	 * @param length number of bytes to be updated
	 */
	public void update (byte buffer[], int length) {
		update(state, buffer, 0, length);
	}

	/**
	 * Updates hash with given array of bytes
	 *
	 * @param buffer array of bytes to use for updating the hash
	 * @param offset offset in array
	 * @param length number of bytes to be updated
	 */
	public void update (byte buffer[], int offset, int length) {
		update(state, buffer, offset, length);
	}

	/**
	 * Updates hash with a single byte
	 *
	 * @param b single byte to update the hash
	 */
	public void update (byte b) {
		update(new byte[]{b}, 1);
	}

	/**
	 * Update buffer with given string.  Note that because the version of
	 * the s.getBytes() method without parameters is used to convert the
	 * string to a byte array, the results of this method may be different
	 * on different platforms.  The s.getBytes() method converts the string
	 * into a byte array using the current platform's default character set
	 * and may therefore have different results on platforms with different
	 * default character sets.  If a version that works consistently
	 * across platforms with different default character sets is desired,
	 * use the overloaded version of the Update() method which takes a
	 * string and a character encoding.
	 *
	 * @param s String to be update to hash (is used as s.getBytes())
	 */
	public void update (String s) {
		byte chars[];
		update(chars = s.getBytes(), chars.length);
	}

	/**
	 * Update buffer with given string using the given encoding.  If the
	 * given encoding is null, the encoding "ISO8859_1" is used.
	 *
	 * @param s            String to be update to hash (is used as s.getBytes(charset_name))
	 * @param charset_name The character set to use to convert s to a byte array, or null (default "ISO8859_1").
	 * @throws java.io.UnsupportedEncodingException
	 *          If the named charset is not supported.
	 */
	public void update (String s, String charset_name) throws java.io.UnsupportedEncodingException {
		byte chars[];
		update(chars = s.getBytes((charset_name == null) ? "ISO8859_1" : charset_name), chars.length);
	}

	/**
	 * Update buffer with a single integer (only & 0xff part is used,as a byte)
	 *
	 * @param i Integer value, which is then converted to byte as i & 0xff
	 */
	public void update (int i) {
		update((byte)(i & 0xff));
	}

	/**
	 * Returns array of bytes (16 bytes) representing hash as of the
	 * current state of this object. Note: getting a hash does not
	 * invalidate the hash object, it only creates a copy of the real
	 * state which is finalized.
	 *
	 * @return	Array of 16 bytes, the hash of all updated bytes
	 */
	public synchronized byte[] result () {
		if (finals == null) {
			MD5State newFinals = new MD5State(state);
			byte bits[];
			int index, padlen;
			int[] count_ints = {(int)(newFinals.count << 3), (int)(newFinals.count >> 29)};
			bits = encode(count_ints, 8);
			index = (int)(newFinals.count & 0x3f);
			padlen = (index < 56) ? (56 - index) : (120 - index);
			update(newFinals, padding, 0, padlen);
			update(newFinals, bits, 0, 8);
			finals = newFinals;
		}
		return encode(finals.state, 16);
	}

	/**
	 * Returns 32-character hex representation of this objects hash
	 *
	 * @return String of this object's hash
	 */
	public String toHex () {
		return StringUtil.toHex(result(), false);
	}

	/**
	 * Updates hash with the bytebuffer given (using at maximum length bytes from that buffer)
	 *
	 * @param state  Which state is updated
	 * @param buffer Array of bytes to be hashed
	 * @param offset Offset to buffer array
	 * @param length Use at maximum `length' bytes (absolute maximum is buffer.length)
	 */
	private void update (MD5State state, byte buffer[], int offset, int length) {
		int index, partlen, i, start;
		finals = null;
		/* Length can be told to be shorter, but not inter */
		if ((length - offset) > buffer.length) {
			length = buffer.length - offset;
		}
		/* compute number of bytes mod 64 */
		index = (int)(state.count & 0x3f);
		state.count += length;
		partlen = 64 - index;
		if (length >= partlen) {
			int[] decode_buf = new int[16];
			if (partlen == 64) {
				partlen = 0;
			} else {
				for (i = 0; i < partlen; i++) {
					state.buffer[i + index] = buffer[i + offset];
				}
				transform(state, state.buffer, 0, decode_buf);
			}
			for (i = partlen; (i + 63) < length; i += 64) {
				transform(state, buffer, i + offset, decode_buf);
			}
			index = 0;
		} else {
			i = 0;
		}
		/* buffer remaining input */
		if (i < length) {
			start = i;
			for (; i < length; i++) {
				state.buffer[index + i - start] = buffer[i + offset];
			}
		}
	}

	/**
	 * Internal decoding using loop decomposition
	 *
	 * @param buffer byte buffer
	 * @param shift  shift incremenet
	 * @param out    int buffer
	 */
	private void decode (byte[] buffer, int shift, int[] out) {
		out[0] = ((int)(buffer[shift] & 0xff)) |
		         (((int)(buffer[shift + 1] & 0xff)) << 8) |
		         (((int)(buffer[shift + 2] & 0xff)) << 16) |
		         (((int)buffer[shift + 3]) << 24);
		out[1] = ((int)(buffer[shift + 4] & 0xff)) |
		         (((int)(buffer[shift + 5] & 0xff)) << 8) |
		         (((int)(buffer[shift + 6] & 0xff)) << 16) |
		         (((int)buffer[shift + 7]) << 24);
		out[2] = ((int)(buffer[shift + 8] & 0xff)) |
		         (((int)(buffer[shift + 9] & 0xff)) << 8) |
		         (((int)(buffer[shift + 10] & 0xff)) << 16) |
		         (((int)buffer[shift + 11]) << 24);
		out[3] = ((int)(buffer[shift + 12] & 0xff)) |
		         (((int)(buffer[shift + 13] & 0xff)) << 8) |
		         (((int)(buffer[shift + 14] & 0xff)) << 16) |
		         (((int)buffer[shift + 15]) << 24);
		out[4] = ((int)(buffer[shift + 16] & 0xff)) |
		         (((int)(buffer[shift + 17] & 0xff)) << 8) |
		         (((int)(buffer[shift + 18] & 0xff)) << 16) |
		         (((int)buffer[shift + 19]) << 24);
		out[5] = ((int)(buffer[shift + 20] & 0xff)) |
		         (((int)(buffer[shift + 21] & 0xff)) << 8) |
		         (((int)(buffer[shift + 22] & 0xff)) << 16) |
		         (((int)buffer[shift + 23]) << 24);
		out[6] = ((int)(buffer[shift + 24] & 0xff)) |
		         (((int)(buffer[shift + 25] & 0xff)) << 8) |
		         (((int)(buffer[shift + 26] & 0xff)) << 16) |
		         (((int)buffer[shift + 27]) << 24);
		out[7] = ((int)(buffer[shift + 28] & 0xff)) |
		         (((int)(buffer[shift + 29] & 0xff)) << 8) |
		         (((int)(buffer[shift + 30] & 0xff)) << 16) |
		         (((int)buffer[shift + 31]) << 24);
		out[8] = ((int)(buffer[shift + 32] & 0xff)) |
		         (((int)(buffer[shift + 33] & 0xff)) << 8) |
		         (((int)(buffer[shift + 34] & 0xff)) << 16) |
		         (((int)buffer[shift + 35]) << 24);
		out[9] = ((int)(buffer[shift + 36] & 0xff)) |
		         (((int)(buffer[shift + 37] & 0xff)) << 8) |
		         (((int)(buffer[shift + 38] & 0xff)) << 16) |
		         (((int)buffer[shift + 39]) << 24);
		out[10] = ((int)(buffer[shift + 40] & 0xff)) |
		          (((int)(buffer[shift + 41] & 0xff)) << 8) |
		          (((int)(buffer[shift + 42] & 0xff)) << 16) |
		          (((int)buffer[shift + 43]) << 24);
		out[11] = ((int)(buffer[shift + 44] & 0xff)) |
		          (((int)(buffer[shift + 45] & 0xff)) << 8) |
		          (((int)(buffer[shift + 46] & 0xff)) << 16) |
		          (((int)buffer[shift + 47]) << 24);
		out[12] = ((int)(buffer[shift + 48] & 0xff)) |
		          (((int)(buffer[shift + 49] & 0xff)) << 8) |
		          (((int)(buffer[shift + 50] & 0xff)) << 16) |
		          (((int)buffer[shift + 51]) << 24);
		out[13] = ((int)(buffer[shift + 52] & 0xff)) |
		          (((int)(buffer[shift + 53] & 0xff)) << 8) |
		          (((int)(buffer[shift + 54] & 0xff)) << 16) |
		          (((int)buffer[shift + 55]) << 24);
		out[14] = ((int)(buffer[shift + 56] & 0xff)) |
		          (((int)(buffer[shift + 57] & 0xff)) << 8) |
		          (((int)(buffer[shift + 58] & 0xff)) << 16) |
		          (((int)buffer[shift + 59]) << 24);
		out[15] = ((int)(buffer[shift + 60] & 0xff)) |
		          (((int)(buffer[shift + 61] & 0xff)) << 8) |
		          (((int)(buffer[shift + 62] & 0xff)) << 16) |
		          (((int)buffer[shift + 63]) << 24);
	}

	/**
	 * Internal transformation
	 *
	 * @param state
	 * @param buffer
	 * @param shift
	 * @param decode_buf
	 */
	private void transform (MD5State state, byte buffer[], int shift, int[] decode_buf) {
		int a = state.state[0];
		int b = state.state[1];
		int c = state.state[2];
		int d = state.state[3];
		int x[] = decode_buf;
		decode(buffer, shift, decode_buf);
		/* Round 1 */
		a += ((b & c) | (~b & d)) + x[0] + 0xd76aa478; /* 1 */
		a = ((a << 7) | (a >>> 25)) + b;
		d += ((a & b) | (~a & c)) + x[1] + 0xe8c7b756; /* 2 */
		d = ((d << 12) | (d >>> 20)) + a;
		c += ((d & a) | (~d & b)) + x[2] + 0x242070db; /* 3 */
		c = ((c << 17) | (c >>> 15)) + d;
		b += ((c & d) | (~c & a)) + x[3] + 0xc1bdceee; /* 4 */
		b = ((b << 22) | (b >>> 10)) + c;

		a += ((b & c) | (~b & d)) + x[4] + 0xf57c0faf; /* 5 */
		a = ((a << 7) | (a >>> 25)) + b;
		d += ((a & b) | (~a & c)) + x[5] + 0x4787c62a; /* 6 */
		d = ((d << 12) | (d >>> 20)) + a;
		c += ((d & a) | (~d & b)) + x[6] + 0xa8304613; /* 7 */
		c = ((c << 17) | (c >>> 15)) + d;
		b += ((c & d) | (~c & a)) + x[7] + 0xfd469501; /* 8 */
		b = ((b << 22) | (b >>> 10)) + c;

		a += ((b & c) | (~b & d)) + x[8] + 0x698098d8; /* 9 */
		a = ((a << 7) | (a >>> 25)) + b;
		d += ((a & b) | (~a & c)) + x[9] + 0x8b44f7af; /* 10 */
		d = ((d << 12) | (d >>> 20)) + a;
		c += ((d & a) | (~d & b)) + x[10] + 0xffff5bb1; /* 11 */
		c = ((c << 17) | (c >>> 15)) + d;
		b += ((c & d) | (~c & a)) + x[11] + 0x895cd7be; /* 12 */
		b = ((b << 22) | (b >>> 10)) + c;

		a += ((b & c) | (~b & d)) + x[12] + 0x6b901122; /* 13 */
		a = ((a << 7) | (a >>> 25)) + b;
		d += ((a & b) | (~a & c)) + x[13] + 0xfd987193; /* 14 */
		d = ((d << 12) | (d >>> 20)) + a;
		c += ((d & a) | (~d & b)) + x[14] + 0xa679438e; /* 15 */
		c = ((c << 17) | (c >>> 15)) + d;
		b += ((c & d) | (~c & a)) + x[15] + 0x49b40821; /* 16 */
		b = ((b << 22) | (b >>> 10)) + c;


		/* Round 2 */
		a += ((b & d) | (c & ~d)) + x[1] + 0xf61e2562; /* 17 */
		a = ((a << 5) | (a >>> 27)) + b;
		d += ((a & c) | (b & ~c)) + x[6] + 0xc040b340; /* 18 */
		d = ((d << 9) | (d >>> 23)) + a;
		c += ((d & b) | (a & ~b)) + x[11] + 0x265e5a51; /* 19 */
		c = ((c << 14) | (c >>> 18)) + d;
		b += ((c & a) | (d & ~a)) + x[0] + 0xe9b6c7aa; /* 20 */
		b = ((b << 20) | (b >>> 12)) + c;

		a += ((b & d) | (c & ~d)) + x[5] + 0xd62f105d; /* 21 */
		a = ((a << 5) | (a >>> 27)) + b;
		d += ((a & c) | (b & ~c)) + x[10] + 0x02441453; /* 22 */
		d = ((d << 9) | (d >>> 23)) + a;
		c += ((d & b) | (a & ~b)) + x[15] + 0xd8a1e681; /* 23 */
		c = ((c << 14) | (c >>> 18)) + d;
		b += ((c & a) | (d & ~a)) + x[4] + 0xe7d3fbc8; /* 24 */
		b = ((b << 20) | (b >>> 12)) + c;

		a += ((b & d) | (c & ~d)) + x[9] + 0x21e1cde6; /* 25 */
		a = ((a << 5) | (a >>> 27)) + b;
		d += ((a & c) | (b & ~c)) + x[14] + 0xc33707d6; /* 26 */
		d = ((d << 9) | (d >>> 23)) + a;
		c += ((d & b) | (a & ~b)) + x[3] + 0xf4d50d87; /* 27 */
		c = ((c << 14) | (c >>> 18)) + d;
		b += ((c & a) | (d & ~a)) + x[8] + 0x455a14ed; /* 28 */
		b = ((b << 20) | (b >>> 12)) + c;

		a += ((b & d) | (c & ~d)) + x[13] + 0xa9e3e905; /* 29 */
		a = ((a << 5) | (a >>> 27)) + b;
		d += ((a & c) | (b & ~c)) + x[2] + 0xfcefa3f8; /* 30 */
		d = ((d << 9) | (d >>> 23)) + a;
		c += ((d & b) | (a & ~b)) + x[7] + 0x676f02d9; /* 31 */
		c = ((c << 14) | (c >>> 18)) + d;
		b += ((c & a) | (d & ~a)) + x[12] + 0x8d2a4c8a; /* 32 */
		b = ((b << 20) | (b >>> 12)) + c;


		/* Round 3 */
		a += (b ^ c ^ d) + x[5] + 0xfffa3942;      /* 33 */
		a = ((a << 4) | (a >>> 28)) + b;
		d += (a ^ b ^ c) + x[8] + 0x8771f681;      /* 34 */
		d = ((d << 11) | (d >>> 21)) + a;
		c += (d ^ a ^ b) + x[11] + 0x6d9d6122;      /* 35 */
		c = ((c << 16) | (c >>> 16)) + d;
		b += (c ^ d ^ a) + x[14] + 0xfde5380c;      /* 36 */
		b = ((b << 23) | (b >>> 9)) + c;

		a += (b ^ c ^ d) + x[1] + 0xa4beea44;      /* 37 */
		a = ((a << 4) | (a >>> 28)) + b;
		d += (a ^ b ^ c) + x[4] + 0x4bdecfa9;      /* 38 */
		d = ((d << 11) | (d >>> 21)) + a;
		c += (d ^ a ^ b) + x[7] + 0xf6bb4b60;      /* 39 */
		c = ((c << 16) | (c >>> 16)) + d;
		b += (c ^ d ^ a) + x[10] + 0xbebfbc70;      /* 40 */
		b = ((b << 23) | (b >>> 9)) + c;

		a += (b ^ c ^ d) + x[13] + 0x289b7ec6;      /* 41 */
		a = ((a << 4) | (a >>> 28)) + b;
		d += (a ^ b ^ c) + x[0] + 0xeaa127fa;      /* 42 */
		d = ((d << 11) | (d >>> 21)) + a;
		c += (d ^ a ^ b) + x[3] + 0xd4ef3085;      /* 43 */
		c = ((c << 16) | (c >>> 16)) + d;
		b += (c ^ d ^ a) + x[6] + 0x04881d05;      /* 44 */
		b = ((b << 23) | (b >>> 9)) + c;

		a += (b ^ c ^ d) + x[9] + 0xd9d4d039;      /* 33 */
		a = ((a << 4) | (a >>> 28)) + b;
		d += (a ^ b ^ c) + x[12] + 0xe6db99e5;      /* 34 */
		d = ((d << 11) | (d >>> 21)) + a;
		c += (d ^ a ^ b) + x[15] + 0x1fa27cf8;      /* 35 */
		c = ((c << 16) | (c >>> 16)) + d;
		b += (c ^ d ^ a) + x[2] + 0xc4ac5665;      /* 36 */
		b = ((b << 23) | (b >>> 9)) + c;


		/* Round 4 */
		a += (c ^ (b | ~d)) + x[0] + 0xf4292244; /* 49 */
		a = ((a << 6) | (a >>> 26)) + b;
		d += (b ^ (a | ~c)) + x[7] + 0x432aff97; /* 50 */
		d = ((d << 10) | (d >>> 22)) + a;
		c += (a ^ (d | ~b)) + x[14] + 0xab9423a7; /* 51 */
		c = ((c << 15) | (c >>> 17)) + d;
		b += (d ^ (c | ~a)) + x[5] + 0xfc93a039; /* 52 */
		b = ((b << 21) | (b >>> 11)) + c;

		a += (c ^ (b | ~d)) + x[12] + 0x655b59c3; /* 53 */
		a = ((a << 6) | (a >>> 26)) + b;
		d += (b ^ (a | ~c)) + x[3] + 0x8f0ccc92; /* 54 */
		d = ((d << 10) | (d >>> 22)) + a;
		c += (a ^ (d | ~b)) + x[10] + 0xffeff47d; /* 55 */
		c = ((c << 15) | (c >>> 17)) + d;
		b += (d ^ (c | ~a)) + x[1] + 0x85845dd1; /* 56 */
		b = ((b << 21) | (b >>> 11)) + c;

		a += (c ^ (b | ~d)) + x[8] + 0x6fa87e4f; /* 57 */
		a = ((a << 6) | (a >>> 26)) + b;
		d += (b ^ (a | ~c)) + x[15] + 0xfe2ce6e0; /* 58 */
		d = ((d << 10) | (d >>> 22)) + a;
		c += (a ^ (d | ~b)) + x[6] + 0xa3014314; /* 59 */
		c = ((c << 15) | (c >>> 17)) + d;
		b += (d ^ (c | ~a)) + x[13] + 0x4e0811a1; /* 60 */
		b = ((b << 21) | (b >>> 11)) + c;

		a += (c ^ (b | ~d)) + x[4] + 0xf7537e82; /* 61 */
		a = ((a << 6) | (a >>> 26)) + b;
		d += (b ^ (a | ~c)) + x[11] + 0xbd3af235; /* 62 */
		d = ((d << 10) | (d >>> 22)) + a;
		c += (a ^ (d | ~b)) + x[2] + 0x2ad7d2bb; /* 63 */
		c = ((c << 15) | (c >>> 17)) + d;
		b += (d ^ (c | ~a)) + x[9] + 0xeb86d391; /* 64 */
		b = ((b << 21) | (b >>> 11)) + c;

		state.state[0] += a;
		state.state[1] += b;
		state.state[2] += c;
		state.state[3] += d;
	}

	/**
	 * Internal encode
	 *
	 * @param input
	 * @param len
	 * @return
	 */
	private byte[] encode (int input[], int len) {
		int i, j;
		byte out[] = new byte[len];
		for (i = j = 0; j < len; i++, j += 4) {
			out[j] = (byte)(input[i] & 0xff);
			out[j + 1] = (byte)((input[i] >>> 8) & 0xff);
			out[j + 2] = (byte)((input[i] >>> 16) & 0xff);
			out[j + 3] = (byte)((input[i] >>> 24) & 0xff);
		}
		return out;
	}

	//--------------------------------------------------------//
	//                        STATIC METHODS                  //
	//--------------------------------------------------------//

	/**
	 * Return the MD5 representation of the given object generated form the <i>obj.toString()</i> representation
	 *
	 * @param obj object to be used for MD5 hash
	 * @return MD5 representation of the specified object
	 */
	public static final String getMD5 (Object obj) {
		return new MD5(obj).toHex();
	}

	/**
	 * Return the MD5 representation for the given array
	 *
	 * @param buffer array of bytes to use for updating the hash
	 * @return MD5 representation of the specified object
	 */
	public static final String getMD5 (byte[] buffer) {
		return new MD5(buffer, 0, buffer.length).toHex();
	}

	/**
	 * Return the MD5 representation for the given sub-array
	 *
	 * @param buffer array of bytes to use for updating the hash
	 * @param length number of bytes to be updated
	 * @return MD5 representation of the specified object
	 */
	public static final String getMD5 (byte[] buffer, int length) {
		return new MD5(buffer, 0, length).toHex();
	}

	/**
	 * Return the MD5 representation for the given sub-array
	 *
	 * @param buffer array of bytes to use for updating the hash
	 * @param offset offset in array
	 * @param length number of bytes to be updated
	 * @return MD5 representation of the specified object
	 */
	public static final String getMD5 (byte[] buffer, int offset, int length) {
		return new MD5(buffer, offset, length).toHex();
	}

	//--------------------------------------------------------//
	//                       INNER CLASSES                    //
	//--------------------------------------------------------//

	/**
	 * Internal class used to maintain the MD5 encoding state
	 */
	private static class MD5State {

		//--------------------------------------------------------//
		//                          FIELDS                        //
		//--------------------------------------------------------//

		int state[]; // 128-bit state
		long count; // 64-bit character count
		byte buffer[]; // 64-byte buffer (512 bits) for storing to-be-hashed characters

		//--------------------------------------------------------//
		//                      CONSTRUCTORS                      //
		//--------------------------------------------------------//

		/**
		 * Default constructor
		 */
		MD5State () {
			count = 0;
			buffer = new byte[64];
			state = new int[4];
			state[0] = 0x67452301;
			state[1] = 0xefcdab89;
			state[2] = 0x98badcfe;
			state[3] = 0x10325476;
		}

		/**
		 * Copy constructor
		 *
		 * @param copy state to be copied
		 */
		MD5State (MD5State copy) {
			this();
			int i = buffer.length;
			while (i-- != 0) {
				buffer[i] = copy.buffer[i];
			}
			i = state.length;
			while (i-- != 0) {
				state[i] = copy.state[i];
			}
			count = copy.count;
		}
	}

}
